package com.zjxh.zjxh.common.aop;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.zjxh.zjxh.common.OperationLog;
import com.zjxh.zjxh.common.Result;
import com.zjxh.zjxh.common.constant.CMEnum;
import com.zjxh.zjxh.common.constant.Constant;
import com.zjxh.zjxh.common.exception.CommonException;
import com.zjxh.zjxh.common.util.RequestUtil;
import java.lang.reflect.Method;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

/**
 * @description: 日志
 * @author: oyp @Date: 2020/4/20 on 3:51 下午
 */
@Component
@Order(1)
@Aspect
@Slf4j
public class LogAspect {

  private final ThreadLocal<OperationLog> aopLogBuilder = new ThreadLocal<>();

  /** 切入点 @Pointcut("execution(public * cn.nocov.hospital.*.controller.*.*(..))") */
  // @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
  @Pointcut("@annotation(ApiLog)")
  public void webLog() {}

  /**
   * 正常:Around-After-AfterReturning
   *
   * @param pjp
   * @throws NoSuchMethodException
   */
  @Around("webLog()")
  public Object around(ProceedingJoinPoint pjp) throws Throwable {
    OperationLog builder = new OperationLog().setTimeCost(System.currentTimeMillis());
    // 其它请求信息
    HttpServletRequest request = RequestUtil.getRequest();
    String userId = request.getHeader(Constant.HEADER_USER_ID);
    MDC.put(Constant.HEADER_USER_ID, userId);
    builder
        .setIp(request.getHeader(Constant.HEADER_IP))
        .setUserId(userId)
        .setUsername(URLUtil.decode(request.getHeader(Constant.HEADER_USERNAME)))
        .setRequestUri(request.getRequestURI())
        .setRequestMethod(request.getMethod());
    aopLogBuilder.set(builder);
    return pjp.proceed();
  }

  /**
   * 正常:Around-After-AfterReturning
   *
   * @param jp
   */
  @After("webLog()")
  public void after(JoinPoint jp) {
    // OperationLog builder = aopLogBuilder.get();
    // builder
    //     .setParams("getReqParams(jp)")
    //     .setTimeCost(System.currentTimeMillis() - builder.getTimeCost());
  }

  /**
   * 正常:Around-After-AfterReturning
   *
   * @param joinPoint
   * @param jsonResult
   */
  @AfterReturning(returning = "jsonResult", pointcut = "webLog()")
  public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
    if (jsonResult != null) {
      aopLogBuilder
          .get()
          .setResult(jsonResult)
          .setCode(((Result<Object>) jsonResult).getCode())
          .setMsg(((Result<Object>) jsonResult).getMsg());
    }
    handleLog(joinPoint);
  }

  /**
   * 异常:Around-After-AfterThrowing
   *
   * @param joinPoint
   * @param e
   */
  @AfterThrowing(value = "webLog()", throwing = "e")
  public void afterThrow(JoinPoint joinPoint, Exception e) {
    String code;
    String msg;
    if (e instanceof CommonException) {
      CommonException e1 = (CommonException) e;
      code = e1.getCode();
      msg = e1.getMsg();
    } else {
      if (e instanceof ConstraintViolationException) {
        ConstraintViolationException e1 = (ConstraintViolationException) e;
        msg =
            StringUtils.join(
                e1.getConstraintViolations().stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.toList()),
                "、");
        code = CMEnum.ERROR_ARGS_VERIFY.getCode();
      } else {
        code = CMEnum.ERROR.getCode();
        msg = CMEnum.ERROR.getMsg();
      }
    }
    aopLogBuilder.get().setResult(new Result<>().setCode(code).setMsg(msg));
    handleLog(joinPoint);
  }

  /** 日志打印及入库 */
  private void handleLog(final JoinPoint joinPoint) {
    ApiLog apiOperation = getApiOperation(joinPoint);
    OperationLog operationLog = aopLogBuilder.get();
    // operationLog.setModuleName(appName);
    operationLog.setApiDesc(apiOperation != null ? apiOperation.value() : "");
    operationLog.setParams(getReqParams(joinPoint));
    operationLog.setResult(
        operationLog.getResult() != null
            ? StrUtil.sub(operationLog.getResult().toString(), 0, 1000)
            : null);
    // 日志打印以及入库
    log.info(JSON.toJSONString(operationLog, true));
    // if (apiOperation != null && apiOperation.isSaveLog()) {
    //   ThreadUtil.execAsync(
    //       () -> {
    //         if (!"/sys-operation-log/inside-operation-log".equals(operationLog.getRequestUri()))
    // {
    //           SysOperationLogReq req = new SysOperationLogReq();
    //           BeanUtil.copyProperties(operationLog, req);
    //           req.setTimeCost(Math.toIntExact(operationLog.getTimeCost()));
    //         }
    //       });
    // }
    aopLogBuilder.remove();
  }

  /** 是否存在注解，如果存在就获取 */
  private ApiLog getApiOperation(JoinPoint joinPoint) {
    Signature signature = joinPoint.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    Method method = methodSignature.getMethod();
    if (method != null) {
      return method.getAnnotation(ApiLog.class);
    }
    return null;
  }

  /**
   * 获取参数
   *
   * @param joinPoint fs
   * @return 返回
   */
  private String getReqParams(JoinPoint joinPoint) {
    StringBuffer reqParams = new StringBuffer();
    HttpServletRequest req = RequestUtil.getRequest();
    if (req != null) {
      if (req.getMethod().equals(cn.hutool.http.Method.GET.name())) {
        reqParams.append(JSONUtil.toJsonStr(req.getParameterMap()));
      } else {
        for (Object arg : joinPoint.getArgs()) {
          if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
            continue;
          }
          if (arg instanceof MultipartFile) {
            arg = "[MultipartFile]&";
          }
          reqParams.append(arg);
        }
      }
    }
    return reqParams.length() > 1000
        ? reqParams.substring(Constant.INT_ZERO, 1000) + "......"
        : String.valueOf(reqParams);
  }
}
